// JavaScript Document

function assert(bCondition, sErrorMsg) 
{
	if (!bCondition) 
	{
		log.error("assert failed. " + sErrorMsg);
       	throw new Error(sErrorMsg);
	}
} 

function getRandInt(a, b)
{
	return Math.floor((Math.random() * (b - a) + a));
}

// Point and Rect just write, not test still.

function Point(x, y)
{
    if (this instanceof Point)
    {
        if (arguments.length == 0)
        {
            this.x = 0;
            this.y = 0;
        }
        else
        {
            this.x = x;
            this.y = y;
        }
    }
    else
    {
        return new Point(x, y);
    }
}

Point.s_add = function(l, r)
{
    return new Point(l.x + r.x, l.y + r.y);
}

Point.s_sub = function(l, r)
{
    return new Point(l.x - r.x, l.y - r.y);
}

Point.s_mul = function(pt, val)
{
    return new Point(pt.x * val, pt.y * val);
}

Point.s_div = function(pt, val)
{
    return new Point(pt.x / val, pt.y / val);
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
}

Point.prototype.distance = function(pt)
{
	return Math.sqrt((pt.x - this.x) * (pt.x - this.x) + (pt.y - this.y) * (pt.y - this.y));
}

Point.prototype.add = function(pt)
{
    this.x += pt.x;
    this.y += pt.y;
}

Point.prototype.sub = function(pt)
{
    this.x -= pt.x;
    this.y -= pt.y;
}

Point.prototype.mul = function(val)
{
    this.x *= val;
    this.y *= val;
}

Point.prototype.div = function(val)
{
    this.x /= val;
    this.y /= val;
}

Point.prototype.setValue = function(pt)
{
    this.x = pt.x;
    this.y = pt.y;
}

function Rect(x, y, dx, dy)
{
    if (this instanceof Rect)
    {
        if (arguments.length == 0)
        {
            this.x = 0;
            this.y = 0;
            this.dx = 0;
            this.dy = 0;
        }
        else
        {
            this.x = x;
            this.y = y;
            this.dx = dx;
            this.dy = dy;
        }
    }
    else
    {
        return new Rect(x, y, dx, dy);
    }
}

Rect.s_addPoint = function(rt, pt)
{
    return new Rect(rt.x + pt.x, rt.y + pt.y, rt.dx, rt.dy);
}

Rect.s_subPoint = function(rt, pt)
{
    return new Rect(rt.x - pt.x, rt.y - pt.y, rt.dx, rt.dy);
}

Rect.s_addRect = function(l, r)
{
    var _ret = l.clone();
    _ret.addRect(r);
    return _ret;
}

Rect.s_intersectRect = function(l, r)
{
    var _ret = l.clone();
    _ret.intersectRect(r);
    return _ret;
}

Rect.s_isContainRect = function(l, r)
{
    if (l.x > r.x) return false;
    if (l.x + l.dx < r.x + r.dx) return false;
    if (l.y > r.y) return false;
    if (l.y + l.dy < r.y + r.dy) return false;
    return true;
}

Rect.prototype.clone = function(rt)
{
    return new Rect(rt.x, rt.y, rt.dx, rt.dy);
}

Rect.prototype.setValue = function(rt)
{
    this.x = rt.x;
    this.y = rt.y;
    this.dx = rt.dx;
    this.dy = rt.dy;
}

Rect.prototype.isEmpty = function()
{
    return this.dx <= 0 || this.dy <= 0;
}

Rect.prototype.addPoint = function(pt)
{
    this.x += pt.x;
    this.y += pt.y;
}

Rect.prototype.subPoint = function(pt)
{
    this.x -= pt.x;
    this.y -= pt.y;
}

Rect.prototype.addRect = function(rt)
{
    if (rt.isEmpty()) return;
    if (this.isEmpty())
    {
        this.x = rt.x;
        this.y = rt.y;
        this.dx = rt.dx;
        this.dy = rt.dy;
        return;
    }
        
    var right = Math.max(this.x + this.dx, rt.x + rt.dx);
    var bottom = Math.max(this.y + this.dy, rt.y + rt.dy);
    
    this.x = Math.min(this.x, rt.x);
    this.y = Math.min(this.y, rt.y);

    this.dx = right - this.x;
    this.dy = bottom - this.y;
}

Rect.prototype.intersectRect = function(rt)
{
    var right = Math.min(this.x + this.dx, rt.x + rt.dx);
    var bottom = Math.min(this.y + this.dy, rt.y + rt.dy);
    
    this.x = Math.max(this.x, rt.x);
    this.y = Math.max(this.y, rt.y);
    

    this.dx = right > this.x ? (right - this.x) : 0;
    this.dy = bottom > this.y ? (bottom - this.y) : 0;
}

Rect.prototype.isIntersect = function(rt)
{
    if (Math.max(this.x, rt.x) >= Math.min(this.x + this.dx, rt.x + rt.dx))
        return false;
    if (Math.max(this.y, rt.y) >= Math.min(this.y + this.dy, rt.y + rt.dy))
        return false;
    return true;
}

Rect.prototype.hitTest = function(pt)
{
    if (pt.x < this.x || pt.x > (this.x + this.dx))
        return false;
    if (pt.y < this.y || pt.y > (this.y + this.dy))
        return false;
    return true;
}

// add remove function to Array

Array.prototype.remove = function(obj) 
{ 
    var idx = this.indexOf(obj); 
    if (idx >= 0) 
    { 
        this.splice(idx, 1); 
        return true; 
    } 
    return false; 
}

function curry_bind(fn, context)
{
    var args = Array.prototype.slice.call(arguments, 2);
    return function()
    {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(context, finalArgs);
    }
}

function bind(fn, context)
{
    return function()
    {
        return fn.apply(context, arguments);
    }
}

function curry(fn)
{
    var args = Array.prototype.slice.call(arguments, 1);
    return function()
    {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    }
}

function object(o) 
{
    function F(){}
    F.prototype = o;
    return new F();
}

function inheritPrototype(subType, superType)
{
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

var event_default = "_default";

function ListenObject()
{
    if (this instanceof ListenObject)
    {
        this.eventListeners = {};
    }
    else
    {
        return new ListenObject();
    }
}

ListenObject.prototype.registerListener = function(eventType, cb)
{
	assert(cb, "must have listener and cb");
	var _e = event_default;
	if (eventType !== undefined)
	{
		_e = eventType;
	}
	if (!this.eventListeners[_e])
	{
		this.eventListeners[_e] = [];
	}
	this.eventListeners[_e].push(cb);
}

ListenObject.prototype.unregisterListener = function(eventType, cb)
{
	var _e = event_default;
	if (eventType !== undefined)
	{
		_e = eventType;
	}
	if (this.eventListeners[_e])
	{
		var list = this.eventListeners[_e];
        list.remove(cb);
	}
	else
	{
		assert(false, "unregisterListener: not registered, but unregister.");
	}
}

ListenObject.prototype.updateListeners = function(eventType)
{
	if (eventType === undefined)
	{
		assert(false, "updateListeners must pass event type");
	}
	var list = this.eventListeners[eventType];
    if (!list) return;
    for (var i = 0; i < list.length; i++)
    {
        var cb = list[i];
        var args = Array.prototype.slice.call(arguments, 1);
        cb.apply(null, args);
    }
}